//-------------------------------------------------------------------
//	Copyright (c) 2013  Zhirnov Andrey
//	This file is part of the "Noise Library" project.
//	See copyright notice in "linNoiseCL.h".
//-------------------------------------------------------------------

#pragma once

#include "../CSource.h"
#include "GeneratorUtils.h"
#include "GeneratorNativeUtils.h"

namespace Noise
{
	
    const real DEFAULT_CYLINDERS_FREQUENCY = 1.0;


	//
	// Function Source for Cylinders generation
	//

	class CSourceCylinders : public CSource
	{
	// types
	protected:
		typedef CSourceCylinders	Self;


	// variables
	protected:
        real		_frequency;		// Frequency of the concentric cylinders.
		
		static const
			ESource	_sourceType = CYLINDERS;

	// methods
	public:
		CSourceCylinders () : _frequency(DEFAULT_CYLINDERS_FREQUENCY)
		{}

		~CSourceCylinders ()
		{}

		ESourceState GetState () const
		{
			return SOURCE_COMPLETE;
		}
		
		virtual void Delete ()
		{
		}

		virtual EResult GetInteger (ESourceQuery type, int *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case SOURCE_STATE :
					v[0] = GetState();
					return OK;

				case SOURCE_TYPE :
					v[0] = _sourceType;
					return OK;

				case SOURCES_COUNT :
					v[0] = 1;
					return OK;

				case INPUT_SOURCES :
					v[0] = 0;
					return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult SetArg (EArgument type, const real *v, int count)
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_FREQUENCY		: _frequency = v[0];	return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual EResult GetArg (EArgument type, real *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case ARG_FREQUENCY		: v[0] = _frequency;	return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual CSourcePtr Clone () const override
		{
			Self *	src = new Self();
			src->_frequency = _frequency;
			return src;
		}
		
		virtual void PreBuild (const TPreBuildData &data) override
		{
		}

		virtual bool BuildFunction (TFunctionData &data) override
		{
			Utils::NoiseGenFunctions( data );

			if ( data.AddSourceType( _sourceType ) )
			{
				data.source << TOSTRING(
					real Cylinders (real3 vPoint, real _frequency)\n
					{\n
						real x = vPoint.x * _frequency;\n
						real z = vPoint.z * _frequency;\n
						real distFromCenter = sqrt( x * x + z * z );\n
						real distFromSmallerSphere = distFromCenter - floor( distFromCenter );\n
						real distFromLargerSphere = (real)(1.0) - distFromSmallerSphere;\n
						real nearestDist = min( distFromSmallerSphere, distFromLargerSphere );\n
						return (real)(1.0) - ( nearestDist * (real)(4.0) );\n
					}\n\n
				);
			}

			CSource::BuildFunctionHeader( data.source, _id )
				<< " ( Cylinders( vPoint, " << _frequency << " ) )\n";

			return true;
		}
		
		NATIVE(
		template < typename Real, Real (CSource::*NativeFunction)(const TVec<Real,3> &) >
		Real NativeFunction (const TVec<Real,3> &vPoint)
		{
			Real x = vPoint.x * _frequency;
			Real z = vPoint.z * _frequency;
			Real distFromCenter = Sqrt( x * x + z * z );
			Real distFromSmallerSphere = distFromCenter - Floor( distFromCenter );
			Real distFromLargerSphere = Real(1.0) - distFromSmallerSphere;
			Real nearestDist = Min( distFromSmallerSphere, distFromLargerSphere );
			return Real(1.0) - ( nearestDist * Real(4.0) );
		}

		virtual float32 NativeFunction32 (const fvec3 &vPoint) override
		{
			return NativeFunction< float32, &CSource::NativeFunction32 >( vPoint );
		}

		virtual float64 NativeFunction64 (const dvec3 &vPoint) override
		{
			return NativeFunction< float64, &CSource::NativeFunction64 >( vPoint );
		})

		static bool CreateSource (CSourcePtr &src, CContext *context, ESource type)
		{
			ASSERT( type == _sourceType );

			src = new Self();
			return true;
		}
	};

}	// Noise